package main

//全局互斥锁解决资源竞争问题

//通道
import "fmt"

// import "reflect"
import "sync"
import "time"
import "strconv"

var wg sync.WaitGroup

var (
	//指定map的初始容量
	myMap = make(map[int]int, 200)
)

func myfun(mychnl chan string) {
	for i := 0; i < 4; i++ {
		appendStr := fmt.Sprintf("chan=%d", i)
		mychnl <- appendStr
	}
	close(mychnl)
}

// 洗菜
func washVegetables() <-chan string {
	vegetables := make(chan string)
	go func() {
		time.Sleep(5 * time.Second)
		vegetables <- "洗好的菜"
	}()
	return vegetables
}

// 烧水
func boilWater() <-chan string {
	water := make(chan string)
	go func() {
		time.Sleep(5 * time.Second)
		water <- "水烧好了"
	}()
	return water
}

func goRoutineTest() {
	ch := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			ch <- i
		}
		close(ch)
	}()
	for {
		data, ok := <-ch
		if !ok {
			fmt.Println("ok的值是：", ok)
			//break
		}
		fmt.Println(data)
	}
	/*总结：这段代码中，通过循环读取通道中的数据，并判断通道是否已经被关闭。
	当通道被关闭，读取操作将会失败，ok的值将会变为false,从而退出循环。
	*/
}

func sameTimeOne() {
	ch := make(chan int)
	ch <- 1 // 由于消费者还没执行到，这里会一直阻塞住
	<-ch
	//死锁了
}

func defaultTest() {
	result := make(chan string, 1)
	result <- "服务端结果"

	v := <-result
	fmt.Println(v)
	/*
		go func() {
			result <- "服务端结果"
		}()

		select {
		case v := <-result:
			fmt.Println(v)
		case <-time.After(5 * time.Second):
			fmt.Println("网络访问超时了")
		}
	*/
}

func main() {
	//sameTimeOne()
	//defaultTest()
	//wcQuestion()

	chan1 := make(chan string)
	chan2 := make(chan string)
	go func() {
		fmt.Println("select start")
		select {
		case msg1, ok := <-chan1:
			if !ok {
				fmt.Println("ch1 closed")
				return
			} else {
				fmt.Println("msg1:", msg1)
			}

		case msg2, ok := <-chan2:
			if !ok {
				fmt.Println("ch2 closed")
				return
			} else {
				fmt.Println("msg2:", msg2)
			}

		default:
			fmt.Println("select default")
		}
		fmt.Println("select end")
	}()
	chan1 <- "Hello Select"
	chan2 <- "hello select2"
	time.Sleep(time.Second)
	fmt.Println("main end")
	/*
		result := make(chan string)
		go func() {
			time.Sleep(8 * time.Second)

		}()
		//close(result)
		select {
		case v := <-result:
			fmt.Println(v)
			//defer close(result)

		case <-time.After(5 * time.Second):
			fmt.Println("网络访问超时了")
		}
	*/

	//goRoutineTest()

	/*
		//golang怎么将通道关闭 https://www.php.cn/faq/499975.html
			c := make(chan string)
			go myfun(c)
			for {
				res, ok := <-c
				if ok == false {
					fmt.Println("通道关闭", ok)
					//defer close(c)
					break
				}
				fmt.Println("通道打开", res, ok)
			}

			ch1 := make(chan string, 1)
			ch2 := make(chan string, 1)
			receive(ch1, "pass message")
			send(ch1, ch2)
			fmt.Println(<-ch1)
	*/

	/*
		ch := make(chan string)       //非缓冲通道
		chBool := make(chan bool, 0)  //非缓冲通道
		chStr := make(chan string, 2) //缓冲通道
		//需要注意的是，程序中必须同时有不同的goroutine对非缓冲通道进行发送和接受操作，否则会造成阻塞。

		ch <- x //通道接收数据x
		x <- ch //通道发送数据并赋值给x
		<- ch //通道 发送数据，忽略接受者

	*/

	//切片
	//s := make([]string, 3)
	//slice := make([]string, 5)

	/*
		vagetablesCh := washVegetables() // 洗菜
		waterCh := boilWater()           // 烧水
		fmt.Println("已经安排洗菜和烧水了，休息一下")
		time.Sleep(3 * time.Second)
		fmt.Println("要做饭了，看看菜和水好了吗")
		vegetables := <-vagetablesCh
		water := <-waterCh
		fmt.Println("准备好了，可以做饭了", vegetables, water)
	*/

	/*
		s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
		s = append(s, 100)
		fmt.Println("slice切片为：", s)
		//append(keys,key)

		array := [...]int{1, 2, 3, 4, 100}
		//数字写法一
		//var array [5]int
		//array[4] = 100
		fmt.Println("array为：", array)

		printNumber()
		//PrintNum()
		//sskTest(1000)

		ch := make(chan int)
		wg.Add(1)
		go func() {
			defer wg.Done()
			ch <- 100
			//ch <- 200
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			r := <-ch
			fmt.Println("r", r)
		}()
		wg.Wait()
	*/

	//deadlock!
	/*
		//获取当前系统cpu的数量
		num := runtime.NumCPU()
		//我们这里设置num-1的cpu运行go程序
		runtime.GOMAXPROCS(num)
		fmt.Println("num=", num)

		wg.Add(1)
		for i := 1; i < 200; i++ {
			go factorialSum(i)
		}
		wg.Wait()

		for i, v := range myMap {
			fmt.Printf("map[%d]=%d\n", i, v)
		}
	*/

	//我们开启了协程，但是这个协程和我们的主线程是同一时间执行的，所以，
	//map还没有赋值，主线程就已经执行到了for i,v := range myMap这种地方

	/*
		go test()
	*/

	/*
		b := 100
		fmt.Println("b:",b)
		fmt.Println("type:",reflect.TypeOf(b))
	*/

	/*
		//使用 make(chan val-type) 创建一个新的通道
		messages := make(chan string)

		go func() {
			//使用 channel <- 语法 发送 一个新的值到通道中。
			//这里我们在一个新的 Go 协程中发送 "ping" 到上面创建的messages 通道中。
			messages <- "ping"
		}()

		//使用 <-channel 语法从通道中 接收 一个值。
		msg := <-messages
		fmt.Println("msg:", msg)
	*/

	/*我们运行程序时，通过“通道”，消息“ping”成功的从一个Go协程传到另一个中
	默认发送和接收是阻塞的，直到发送和接收方都准备完毕。这个特性允许我们，不适用任何其它的同步操作，
	来在程序结尾等待消息“ping”.
	*/

	//练习——通道：是用来在协程之间传递数据
	/*
		ch := make(chan int)
		//设置通道缓冲值为2
		wg.Add(1)
		go func() {
			defer wg.Done()
			ch <- 10
			ch <- 19
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			r := <-ch
			fmt.Println("r:", r)
		}()
		wg.Wait()
	*/
}

// 厕所问题：https://www.bilibili.com/video/BV1ce4y1g7dZ/?spm_id_from=333.337.search-card.all.click&vd_source=981ecfa8175498bbb21623e15803c777
func wcQuestion() {
	wc1 := make(chan struct{})
	wc2 := make(chan struct{})
	go func() {
		select {
		case <-wc1:
			fmt.Println("to 1 hao")
		case <-wc2:
			fmt.Println("to 2 hao")
			//疑问❓：这里把default对应判断语句全去掉
		default:
			fmt.Println("default operation")
		}
		fmt.Println("other operation")
	}()
	time.Sleep(3 * time.Second)
	wc1 <- struct{}{}
}

// 单向channel
func receive(receiver chan<- string, msg string) {
	receiver <- msg
}

func send(asender <-chan string, receiver chan<- string) {
	msg := <-asender
	receiver <- msg
}

func sskTest(number int) {
	func(v int) {
		fmt.Println("for循环i的结果为", v)
	}(number)
}

func printNumber() {
	for i := 0; i < 5; i++ {
		defer func() {
			fmt.Println("for循环i的结果为", i)
		}()
	}
}

func PrintNum() {
	for i := 0; i < 5; i++ {
		defer func(v int) {
			fmt.Println("for循环i的结果为", v)
		}(i)
	}
	//这里注意两个点，1、匿名函数的i赋值给变量v
	//2、多个defer出现，对应执行顺序问题？（先进后出原则）
}

func test() {
	// defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Println("test:hello world" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

// 阶乘
func factorialSum(n int) {
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}
	myMap[n] = res
}
